/**
 * Aptana Studio
 * Copyright (c) 2005-2012 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
 * Please see the license.html included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.index.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;

import com.aptana.core.IMap;
import com.aptana.core.logging.IdeLog;
import com.aptana.core.util.ArrayUtil;
import com.aptana.core.util.CollectionsUtil;
import com.aptana.jetty.util.epl.ajax.JSON.Convertible;

/**
 * IndexUtil
 */
public class IndexUtil
{
	/**
	 * This method tries to cast the specified object to an array. If that is successful, then a new list will be
	 * generated. Each array element that is a Map will be passed into the fromJSON on the new instance of T.
	 * 
	 * @param <T>
	 * @param object
	 * @param list
	 * @param itemClass
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static <T extends Convertible> List<T> createList(Object object, Class<T> itemClass)
	{
		List<T> result = null;

		if (itemClass != null && object != null)
		{
			if (object.getClass().isArray())
			{
				Object[] objects = (Object[]) object;

				if (objects.length > 0)
				{
					result = new ArrayList<T>(objects.length);

					// Wrap loop because if newInstance fails once, it will fail for all iterations
					try
					{
						for (Object value : objects)
						{
							if (value instanceof Map)
							{
								T v = itemClass.newInstance();

								v.fromJSON((Map) value);

								result.add(v);
							}
						}
					}
					catch (InstantiationException e)
					{
						IdeLog.logError(IndexPlugin.getDefault(), e);
					}
					catch (IllegalAccessException e)
					{
						IdeLog.logError(IndexPlugin.getDefault(), e);
					}
				}
			}
			else if (object instanceof List)
			{
				List objects = (List) object;

				if (objects.size() > 0)
				{
					result = new ArrayList<T>();

					// Wrap loop because if newInstance fails once, it will fail for all iterations
					try
					{
						for (Object value : objects)
						{
							if (value instanceof Map)
							{
								T v = itemClass.newInstance();

								v.fromJSON((Map) value);

								result.add(v);
							}
						}
					}
					catch (InstantiationException e)
					{
						IdeLog.logError(IndexPlugin.getDefault(), e);
					}
					catch (IllegalAccessException e)
					{
						IdeLog.logError(IndexPlugin.getDefault(), e);
					}
				}
			}
		}

		return result;
	}

	/**
	 * This method tries to cast the specified object to an array. If that is successful, then a new list will be
	 * generated by calling toString on each array element as it is added to the resulting list
	 * 
	 * @param object
	 * @param list
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<String> createList(Object object)
	{
		List<String> result = null;
		if (object != null)
		{
			Collection<Object> objects = null;
			if (object.getClass().isArray())
			{
				objects = Arrays.asList((Object[]) object);
			}
			else if (object instanceof Collection)
			{
				objects = (Collection<Object>) object;
			}

			if (!CollectionsUtil.isEmpty(objects))
			{
				return CollectionsUtil.map(objects, new IMap<Object, String>()
				{
					public String map(Object item)
					{
						return item.toString();
					}
				});
			}
		}
		return result;
	}

	/**
	 * Given an {@link IFileStore}, we traverse to add all files underneath it. This method is recursive, traversing
	 * into sub-directories.
	 * 
	 * @param file
	 * @param monitor
	 * @return
	 */
	public static Set<IFileStore> getAllFiles(IFileStore file, IProgressMonitor monitor)
	{
		// TODO We should likely call IFileSystem.fetchTree and use that if it doesn't return null (because that is more
		// efficient in some schemes)!
		SubMonitor sub = SubMonitor.convert(monitor, 10);
		Set<IFileStore> files = new HashSet<IFileStore>(0);
		try
		{
			if (file == null)
			{
				return files;
			}
			IFileInfo info = file.fetchInfo(EFS.NONE, sub.newChild(1));
			if (!info.exists())
			{
				return files;
			}

			// We know it exists...
			if (info.isDirectory())
			{
				// If it's a derived resource, skip it!
				IContainer curr = file.getAdapter(IContainer.class);
				if (curr != null && curr.isDerived())
				{
					return files;
				}
				try
				{
					// Now try to dive into directory and add all children recursively
					IFileStore[] fileList = file.childStores(EFS.NONE, sub.newChild(2));
					if (ArrayUtil.isEmpty(fileList))
					{
						return files;
					}
					for (IFileStore child : fileList)
					{
						files.addAll(getAllFiles(child, sub.newChild(7)));
					}
				}
				catch (CoreException e)
				{
					IdeLog.logError(IndexPlugin.getDefault(), e);
				}
			}
			else
			{
				// it's a file that exists, base case, add it.
				return CollectionsUtil.newSet(file);
			}
		}
		catch (CoreException e)
		{
			IdeLog.logError(IndexPlugin.getDefault(), e);
		}
		finally
		{
			sub.done();
		}
		return files;
	}

	private IndexUtil()
	{
	}
}
